package stars.starSystems;

import events.GetGroupZoneParamsEvent;
import gameAppLayer.enums.Constants;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import old.coordinate.CoordinateModel;

import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;

import controller.IController;


import stars.enums.EStarDistribution;
import stars.enums.EStarSystemType;
import stars.enums.ESystemMod;
import utilities.Key;
import view.AbstractView;
import view.IView;

import model.IModel;
import model.AbstractModel;
import model.PhysicalDataModel;

/*
 * StarSystems hold any system-level information needed by in-system objects
 * 1) Provide incident radiation at any distance in the system
 * 2) Hold keys to all objects in the system (for general updates)(?)
 * 3) Contains distances, orbital relationships, etc. for objects in-system
 * 4) Contains system level infrastructure information
 */

public class StarSystemModel extends AbstractModel {
	
	
	private ArrayList<String> names = new ArrayList<String>();
	private ArrayList<Integer> location = new ArrayList<Integer>();
		
	private float systemEnergy;
	private float systemPopulation;
	//private ArrayList<MState> states = new ArrayList<MState>(); //states with a presence in system
	//private HashSet<Key>  CONTINUE--what other information needs to be displayed re: a star system? 
		
	private String systemLayout;
	private ArrayList<ESystemMod> systemMods = new ArrayList<ESystemMod>(); 
	private HashMap<Key, ArrayList<Double>> habZoneList = new HashMap<Key, ArrayList<Double>>(); //km
	private HashMap<Key, ArrayList<Double>> snowZoneList = new HashMap<Key, ArrayList<Double>>(); //km
	
	private ArrayList<Key> starGroups = new ArrayList<Key>();
	
	private HashMap<Key, Double> starGroupOrbits = new HashMap<Key, Double>();
	private HashMap<Key, Double> starGroupMasses = new HashMap<Key, Double>();
	private HashMap<Key, Double> starGroupRadii = new HashMap<Key, Double>();
	private HashMap<Key, Double> starGroupLuminosities = new HashMap<Key, Double>();
	
	//private Vector3f location;
	//private Vector3f velocity;
	
	//private ArrayList<Key> coordinate = new ArrayList<Key>();
	
	//private AbstractModel model;
	
	public StarSystemModel(ArrayList<Integer> location, String systemLayout, String name) {
		this.location.addAll(location);		
		this.systemLayout = systemLayout;
		this.names.add(name);
		//this.model = new AbstractModel();
	}
	
	public void addStarGroup(Key starGroup) {
		this.starGroups.add(starGroup);
		this.habZoneList.put(starGroup, new ArrayList<Double>());
		this.snowZoneList.put(starGroup, new ArrayList<Double>());
		this.getController().processOutgoingEvent(new GetGroupZoneParamsEvent(starGroup, this.getKey()));
	}
	
	public void removeStarGroup(Key starGroup) {
		this.starGroups.remove(starGroup);
		this.starGroupLuminosities.remove(starGroup);
		this.starGroupMasses.remove(starGroup);
		this.starGroupOrbits.remove(starGroup);
		this.starGroupRadii.remove(starGroup);
		this.habZoneList.remove(starGroup);
		this.snowZoneList.remove(starGroup);
		updateZones();
	}
	
	//When another starGroup or star is added, updates habZones and snowLines
	public void updateZones() {
		habZoneList.clear();
		snowZoneList.clear();		
		
		//Calculate habZones, snowLines for each group
		for (int i = 0; i < starGroups.size()-1; i++) {
			for (int j = i+1; j < starGroups.size(); j++) {
				adjustZones(starGroups.get(i), starGroups.get(j));
			}						
		}
	}
	
	private void adjustZones(Key group1, Key group2) {
		//Calculate HabitableZones
		//---------------------------------------
		ArrayList<Double> gravZeroPoints = new ArrayList<Double>();
		
		double radius1 = starGroupRadii.get(group1);
		double radius2 = starGroupRadii.get(group2);
		
		//Shift orbits to center around 0 point
		double orbit1 = 0.0;
		double orbit2 = starGroupOrbits.get(group2) - starGroupOrbits.get(group1);			
		
		double mass1 = starGroupMasses.get(group1);
		double mass2 = starGroupMasses.get(group2);			
		double combinedMass = mass1 + mass2;
		
		double massTerm = Math.sqrt(mass1/mass2);
		double gravZeroPoint = orbit2*massTerm/(1+massTerm);
		gravZeroPoints.add(gravZeroPoint);
		
		double r1 = gravZeroPoint - orbit1;
		double r2 = orbit2 - gravZeroPoint;
		
		//inner, outer bounds of habZone around group1
		double innerBound1 = Constants.MinHabOrbitKm + radius1;
		double outerBound1 = r1*mass1/combinedMass;
		//inner, outer bounds of habZone around group2
		double innerBound2 = Constants.MinHabOrbitKm + radius2;
		double outerBound2 = r2*mass2/combinedMass;
		
		if (innerBound1 > outerBound1) {
			habZoneList.get(group1).clear();			
		}			
		else if (outerBound1 < habZoneList.get(group1).get(1)) {
			habZoneList.get(group1).set(1, outerBound1);			
		}
		
		if (innerBound2 > outerBound2) {
			habZoneList.get(group2).clear();			
		}			
		else if (outerBound2 < habZoneList.get(group2).get(1)) {
			habZoneList.get(group2).set(1, outerBound2);			
		}
		//---------------------------------------
		
		//Calculate SnowZones
		//---------------------------------------
		double luminosity1 = starGroupLuminosities.get(group1);
		double luminosity2 = starGroupLuminosities.get(group2);
		
		double snowTemp = 160; //K		
		double argument = 1/(4*Math.PI*Constants.BoltzmannConstant*snowTemp*snowTemp*snowTemp*snowTemp);
		double scaleFactor = 2.244; //Correction factor based on our solar system
		
		double snowZoneStart1 = radius1 + Math.sqrt(luminosity1*argument)*Constants.MtoKm/scaleFactor; //km;
		double snowZoneStart2 = radius2 + Math.sqrt(luminosity2*argument)*Constants.MtoKm/scaleFactor; //km;
		
		if (snowZoneStart1 < habZoneList.get(group1).get(1)) {
			snowZoneList.get(group1).set(0, snowZoneStart1);
			snowZoneList.get(group1).set(1, habZoneList.get(group1).get(1));
		}
		else {
			snowZoneList.get(group1).clear();
		}
		
		if (snowZoneStart2 < habZoneList.get(group2).get(1)) {
			snowZoneList.get(group2).set(0, snowZoneStart2);
			snowZoneList.get(group2).set(1, habZoneList.get(group2).get(1));
		}
		else {
			snowZoneList.get(group2).clear();
		}
		//---------------------------------------
	}
	
	

	public void addName(String name) {
		names.add(name);
	}
	
	public String getCurrentName() {
		int index = names.size()-1;
		return names.get(index);
	}

	public ArrayList<String> getNames() {
		return names;
	}

	public void setSystemLayout(String systemLayout) {
		this.systemLayout = systemLayout;
	}

	public String getSystemLayout() {
		return systemLayout;
	}

	public void addSystemMod(ESystemMod systemMod) {
		this.systemMods.add(systemMod);
	}

	public ArrayList<ESystemMod> getSystemMods() {
		return systemMods;
	}
	
	public void deleteSystemMod(ESystemMod systemMod) {
		this.systemMods.remove(systemMod);
	}
	
	public void addStarGroupOrbit(Key key, double orbit) {
		this.starGroupOrbits.put(key, orbit);
	}
	
	public void addStarGroupRadius(Key key, double radius) {
		this.starGroupRadii.put(key, radius);
	}
	
	public void addStarGroupLuminosity(Key key, double lum) {
		this.starGroupLuminosities.put(key, lum);
	}
	
	public void addStarGroupMass(Key key, double mass) {
		this.starGroupMasses.put(key, mass);
	}
	
	public ArrayList<Integer> getLocation() {
		return this.location;
	}
	
	
//	//IModel methods
//	public void attach(IConcreteView view) {
//		model.attach(view);
//	}
//
//	public void detach(IConcreteView view) {
//		model.detach(view);		
//	}
//
//	public void updateViews() {
//		model.updateViews();
//	}
//
//	public void setController(IController controller) {
//		model.setController(controller);
//	}
//
//	public IController getController() {
//		return model.getController();
//	}
//
//	public Key getKey() {
//		return model.getKey();
//	}
//
//	public void setKey(Key key) {
//		model.setKey(key);
//	}
//
//	public int getKeyValue() {
//		return model.getKeyValue();
//	}
	
	
}
